package cn.com.qtgl.sso.oidc.comp.service;

import cn.com.qtgl.apis.builtin.comp.IntrospectConfig;
import cn.com.qtgl.kit.HttpKit;
import cn.com.qtgl.oauth2.ClientType;
import cn.com.qtgl.oauth2.TokenManager;
import cn.com.qtgl.sso.oidc.comp.config.SsoConfig;
import cn.com.qtgl.sso.oidc.comp.config.SsoScopesConfig;
import cn.com.qtgl.sso.oidc.model.vo.SsoToken;
import cn.com.qtgl.sso.oidc.model.vo.TokenMessage;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.melonlib.apis.http.comp.action.ApiRequestAction;
import cn.melonlib.apis.support.comp.config.ApiRequestConfig;
import cn.melonlib.cores.aop.comp.config.SystemConfig;
import cn.melonlib.cores.aop.comp.web.WebInfo;
import cn.melonlib.cores.commons.comp.utils.AdvancedStringUtils;
import cn.melonlib.cores.commons.comp.utils.SecurityUtil;
import cn.melonlib.cores.commons.comp.utils.UriPathUtils;
import cn.melonlib.cores.commons.comp.web.ctrl.BasicController;
import cn.melonlib.objects.maps.JsonMap;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.hibernate.service.spi.ServiceException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

@Service
public class TokenService {

    @Resource
    private TokenManager tokenManager;

    @Resource
    private SsoConfig ssoConfig;

    @Resource
    private SsoScopesConfig ssoScopesConfig;

    @Resource
    private OkHttpClient okHttpClient;

    @Resource
    private ObjectMapper jsonMapper;

    @Resource
    private WebInfo webInfo;

    @Value("${system.config.static_token:none_token}")
    private String staticToken;

    @Value("${system.config.static_tenant:none_tenant}")
    private String staticTenant;


    @Resource
    private SystemConfig systemConfig;

    @Resource
    private SecurityUtil securityUtil;


    @Resource
    private ApiRequestAction apiRequestAction;

    @Resource
    @Lazy
    private IntrospectConfig introspectConfig;

    @Resource
    private UriPathUtils uriPathUtils;

//    @Cacheable(value = "token", key = "'systemToken'")
    public SsoToken getSystemToken(){
        String url=uriPathUtils.getHttpAbsolutePath(ssoConfig.getOauthAuthorityUrl()+"/token");
        String clientId=ssoConfig.getOauthClientId();
        String secret=ssoConfig.getOauthClientSecret();
        String scopes=ssoScopesConfig.getClientCredentials();
        String type="client_credentials";
        System.out.println(url);
        FormBody formBody=new FormBody.Builder().add("client_id",clientId).add("client_secret",secret)
                .add("scope",scopes).add("grant_type",type).build();
        try(Response response=okHttpClient.newCall(new Request.Builder().url(url).post(formBody).build()).execute()){
            String body=response.body().string();
            JsonMap accsessToken=jsonMapper.readValue(body, new TypeReference<JsonMap<Object>>() {
            });
            SsoToken ssoToken=new SsoToken();
            ssoToken.setAccessToken(ObjectUtils.defaultIfNull(accsessToken.get("access_token"),"").toString());
            ssoToken.setScope(ObjectUtils.defaultIfNull(accsessToken.get("scope"),"").toString());
            ssoToken.setTokenType(ObjectUtils.defaultIfNull(accsessToken.get("token_type"),"").toString());
            ssoToken.setExpiresIn(ObjectUtils.defaultIfNull((Number)accsessToken.get("expires_in"),7200).intValue());
            ssoToken.setClientType(ClientType.APPLICATION);
            return ssoToken;
        }catch (Exception e){
            throw new ServiceException(e.getMessage(),e);
        }
    }

    public boolean validToken() throws Exception{
        TokenMessage tokenMessage=introspect();
        return Objects.nonNull(tokenMessage)&&BooleanUtils.isTrue(tokenMessage.getActive());
    }

    public TokenMessage introspect() throws Exception{
        return introspect(getClientToken().getAccessToken());
    }

//    @Cacheable(value = "token", key = "#token")
    public TokenMessage introspect(String token) throws Exception{
        String body=apiRequestAction.post(introspectConfig.getUrl()+"&token="+token,JsonMap.<Object>createMap()
                ,JsonMap.<String>createMap(), BasicController.APPLICATION_JSON_UTF8_VALUE)
                .body();
        return jsonMapper.readValue(body, TokenMessage.class);
    }

    public SsoToken getClientToken() {
        HttpServletRequest request = HttpKit.getCurrentRequest();
        String token = request.getParameter("access_token");
        if (token == null) {
            String authorization = request.getHeader("Authorization");
            token = HttpKit.getAuthHeaderField(authorization, HttpKit.AuthType.Bearer);
        }
        if(token == null){
            token=staticToken;
        }
        SsoToken ssoToken=new SsoToken();
        ssoToken.setAccessToken(token);
        ssoToken.setClientType(ClientType.USER);
        return ssoToken;
    }

    public String getTenant(){
        HttpServletRequest request = HttpKit.getCurrentRequest();
        return AdvancedStringUtils.defaultIfBlank(request.getParameter("domain"),
                ()->request.getParameter("tenant"),
                ()->request.getHeader(ApiRequestConfig.TENANT),
                ()-> {
                    try {
                        return this.introspect().getTenant();
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                },
                ()->staticTenant,()->"");
    }

    /**
     * 获取请求头X-Correlation-ID的值
     */

    public String correlationId(){
        HttpServletRequest request = HttpKit.getCurrentRequest();
        return AdvancedStringUtils.defaultIfBlank(request.getHeader("X-Correlation-ID")
        ,()->request.getParameter("X-Correlation-ID"),()-> systemConfig.getName()+"_"+ IdUtil.fastSimpleUUID(),
                ()->{
                    String clientId=ssoConfig.getOauthClientId();
                    String secret=ssoConfig.getOauthClientSecret();
                    long time=System.currentTimeMillis();
                    String random= RandomUtil.randomString("ABCDEFGHIJKLMNOPQRSTUVWXYZAbcdefghijklmnopqrstuvwxyz0123456789",10);
                    return systemConfig.getName()+"&"+securityUtil.signature(clientId,secret,time,random)+"&"+time+"&"+random;
                }
        );
    }
}
